// tcp_process_server.hpp
#pragma once
#include <functional>
#include <signal.h>
#include "tcp_socket.hpp"

typedef std::function<void(const std::string &req, std::string *resp)> Handler;

// 多进程版本的 Tcp 服务器
class TcpProcessServer
{
public:
    TcpProcessServer(const std::string &ip, uint16_t port) : _ip(ip), _port(port)
    {
        // 需要处理子进程
        signal(SIGCHLD, SIG_IGN);
    }
    void ProcessConnect(const TcpSocket &new_sock, const std::string &ip, uint16_t port, Handler handler)
    {
        int ret = fork();
        if (ret > 0)
        {
            // father
            // 父进程不需要做额外的操作, 直接返回即可.
            // 思考, 这里能否使用 wait 进行进程等待?
            // 如果使用 wait , 会导致父进程不能快速再次调用到 accept, 仍然没法处理多个请求
            // [注意!!] 父进程需要关闭 new_sock
            new_sock.Close();
            return;
        }
        else if (ret == 0)
        {
            // child
            // 处理具体的连接过程. 每个连接一个子进程
            for (;;)
            {
                std::string req;
                bool ret = new_sock.Recv(&req);
                if (!ret)
                {
                    // 当前的请求处理完了, 可以退出子进程了. 注意, socket 的关闭在析构函数中就完成了
                    printf("[client %s:%d] disconnected!\n", ip.c_str(), port);
                    exit(0);
                }
                std::string resp;
                handler(req, &resp);
                new_sock.Send(resp);
                printf("[client %s:%d] req: %s, resp: %s\n", ip.c_str(), port, req.c_str(), resp.c_str());
            }
        }
        else
        {
            perror("fork");
        }
    }
    bool Start(Handler handler)
    {
        // 1. 创建 socket;
        CHECK_RET(_listen_sock.Socket());
        // 2. 绑定端口号
        CHECK_RET(_listen_sock.Bind(_ip, _port));
        // 3. 进行监听
        CHECK_RET(_listen_sock.Listen(5));
        // 4. 进入事件循环
        for (;;)
        {
            // 5. 进行 accept
            TcpSocket new_sock;
            std::string ip;
            uint16_t port = 0;
            if (!_listen_sock.Accept(&new_sock, &ip, &port))
            {
                continue;
            }
            printf("[client %s:%d] connect!\n", ip.c_str(), port);
            ProcessConnect(new_sock, ip, port, handler);
        }
        return true;
    }

private:
    TcpSocket _listen_sock;
    std::string _ip;
    uint64_t _port;
};